﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using DataCenter.Interface;
using DataCenter.Interface.Model;
using DataCenter.Share;

namespace DataCenter.Component.Calculator
{
    public class ArithmeticCalculator : ICalculator
    {
        public ArithmeticCalculator(SqlCommand command, ArithmeticCalculatorEntity entity)
        {
            this._Command = command;
            this._Entity = entity;
        }
        private SqlCommand _Command;
        private ArithmeticCalculatorEntity _Entity;

        private Dictionary<int, ObjectEntity> DictObject;
        private Dictionary<string, Binary> DictBinary;
        private DateTime Time0;
        private DateTime Time1;

        #region IComponent Members

        public bool Initialize()
        {
            return true;
        }

        #endregion

        #region ICalculator Members

        public bool Execute(ObjectEntity objectEntity, Dictionary<int, ObjectEntity> dictObject, DateTime time0, DateTime time1)
        {
            this.Time0 = time0;
            this.Time1 = time1;

            this.DictObject = dictObject;
            this.DictBinary = new Dictionary<string, Binary>();

            ExpressionComputeCallbackDelegate callback =
                new ExpressionComputeCallbackDelegate(Calculate);
            string expression = Algorithm.InfixToPostfix(objectEntity.ObjectKey);

            DateTime curTime = time0.Date;
            while (curTime <= time1)
            {
                BinaryRecord record = objectEntity.GetRecord(curTime);

                Time1 = Utils.GetLastTime(curTime);
                if (record == null)
                {
                    Time0 = curTime.Date;

                    record = new BinaryRecord();
                    record.ObjectId = objectEntity.ObjectId;
                    record.ValueType = objectEntity.ObjectType;
                    record.ValueTime = curTime.Date;
                    objectEntity.AddRecord(record);
                }
                else 
                {
                    if (Utils.IsLastTime(record.ImportTime) || time1 < record.ImportTime)
                    {
                        curTime = curTime.AddDays(1);
                        continue;
                    }

                    Time0 = record.ImportTime;
                    if (record.ValueTime == Utils.TODAY) 
                        Time1 = Utils.NOW;
                }                                

                string result = Algorithm.ExpressionCompute(expression, callback);

                if (!string.IsNullOrEmpty(result))
                {
                    if (result.StartsWith("[") && result.EndsWith("]"))
                    {
                        // 如果用中括号包括，说明为对象值，直接提取即可
                        if (DictBinary.ContainsKey(result))
                        {
                            Binary binary = DictBinary[result];
                            binary.SetFlag(Time0, Time1, Utils.UNREAL_FLAG, Utils.REAL_FLAG);
                            record.BinaryValue = binary;
                        }
                    }
                    else
                    {
                        // 如果不是中括号包括，说明为浮点数，需要重新打包
                        float value = GetValue(result);
                        Binary binary = new DayBinary();
                        binary.SetValue(Time0, Time1, Utils.REAL_FLAG, 0, value);
                        record.BinaryValue = binary;
                    }
                }
                record.UpdateImportTime();
                record.UpdateInterpolateTime();
                //record.IsSubmit = true;

                curTime = curTime.AddDays(1);
            }

            return true;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            
        }

        #endregion

        private string Calculate(Operators oper, string x, string y)
        {
            DateTime curTime = Time0.Date;
            if (x.StartsWith("[") && x.EndsWith("]"))
            {
                Binary binary0 = GetBinaryValue(ref x, curTime);
                if (binary0 != null)
                {
                    if (y.StartsWith("[") && y.EndsWith("]"))
                    {
                        Binary binary1 = GetBinaryValue(ref y, curTime);
                        if (binary1 != null)
                        {
                            binary0.BeginEdit();
                            binary1.BeginEdit();
                            binary0.CalculateFrom(oper, binary1, Time0, Time1);
                        }
                    }
                    else
                    {
                        float value1 = GetValue(y);
                        if (!float.IsNaN(value1))
                        {
                            binary0.Calculate(oper, 0, value1, Time0, Time1);
                        }
                    }
                    return x;
                }
            }
            else
            {
                float value0 = GetValue(x);
                if (!float.IsNaN(value0))
                {
                    if (y.StartsWith("[") && y.EndsWith("]"))
                    {
                        Binary binary1 = GetBinaryValue(ref y, curTime);
                        if (binary1 != null)
                        {
                            binary1.BeginEdit();
                            binary1.Calculate(oper, 0, value0, Time0, Time1);
                        }
                        return y;
                    }
                    else
                    {
                        float value1 = GetValue(y);
                        if (!float.IsNaN(value1))
                        {
                            return Algorithm.Calculate(oper, value0, value1).ToString();
                        }
                    }
                }
            }
            return string.Empty;
        }

        private Binary GetBinaryValue(ref string operand, DateTime curTime)
        {
            if (DictBinary.ContainsKey(operand))
                return DictBinary[operand];

            operand = operand.Replace("[", "");
            operand = operand.Replace("]", "");

            int objectId;
            if (int.TryParse(operand, out objectId))
            {                
                if (DictObject.ContainsKey(objectId))
                {
                    ObjectEntity objectEntity = DictObject[objectId];

                    BinaryRecord record = GetRecord(objectEntity, curTime);
                    Binary binary = (Binary)record.BinaryValue.Clone();

                    operand = "[" + objectId + "@" + DictBinary.Count + "]";
                    DictBinary.Add(operand, binary);

                    return binary;
                }
            }

            return null;
        }

        private BinaryRecord GetRecord(ObjectEntity objectEntity, DateTime valueTime)
        {
            BinaryRecord record = objectEntity.GetRecord(valueTime);
            if (record == null)
                record = objectEntity.SearchRecord(_Command, valueTime, true);
            if (record == null)
            {
                record = new BinaryRecord();
                record.ObjectId = objectEntity.ObjectId;
                record.ValueType = objectEntity.ObjectType;
                record.ValueTime = valueTime;
                record.BinaryValue = new DayBinary();                
            }
            return record;
        }

        private float GetValue(string operand)
        {
            float value;
            if (!float.TryParse(operand, out value))
                return float.NaN;
            return value;
        }
    }
}
